﻿using Common.CommonModel;
using Common.Configuration;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.IdentityModel.Tokens;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Security.Claims;
using System.Text;
using System.Threading.Tasks;
using static QRCoder.PayloadGenerator;

namespace Core.JwtService
{
    public static class JwtExtensions
    {
        private static IConfiguration config = ConfigurationHelper.GetIConfiguration().GetSection("JwtConfig");
        public static void AddJwtService(this IServiceCollection services)
        {
            services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
                .AddJwtBearer(options =>
                {
					options.TokenValidationParameters = new TokenValidationParameters()
                    {
                        ValidateIssuer = config.GetValue<bool>("ValidIssuer"),
                        ValidIssuer = config["Issuer"],
                        ValidateAudience = config.GetValue<bool>("ValidAudience"),
                        ValidAudience = config["Audience"],
                        ValidateLifetime = config.GetValue<bool>("ValidateLifetime"),
                        IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(config["RequestSecurityKey"])),
                        ClockSkew=TimeSpan.Zero
                    };
                    options.Events = new JwtBearerEvents
                    {
                        OnChallenge = context =>
                        {
                            context.HandleResponse();
                            return Task.FromResult(0);
                        },
                        OnAuthenticationFailed = context =>
                        {
                            string payload = string.Empty;
                            if (context.Exception.GetType() == typeof(SecurityTokenExpiredException))
                            {
                                payload = JsonConvert.SerializeObject(new ResponseModel()
                                {
                                    code = ResponseTypeEnum.Overdue,
                                    message = ResponseTypeEnum.Overdue
                                });
                            }
                            else
                            {
                                payload = JsonConvert.SerializeObject(new ResponseModel()
                                {
                                    code = ResponseTypeEnum.IllegalToken,
                                    message = ResponseTypeEnum.IllegalToken
                                });
                            }
                            context.Response.ContentType = "application/json";
                            context.Response.StatusCode = StatusCodes.Status401Unauthorized;
                            context.Response.WriteAsync(payload);
                            return Task.FromResult(0);
                        },
                        OnForbidden = context =>
                        {
                            var payload = JsonConvert.SerializeObject(new ResponseModel()
                            {
                                code = ResponseTypeEnum.NoToken,
                                message = ResponseTypeEnum.NoToken
                            });
                            context.Response.ContentType = "application/json";
                            context.Response.StatusCode = StatusCodes.Status401Unauthorized;
                            context.Response.WriteAsync(payload);
                            return Task.FromResult(0);
                        },
                        
                    };
                });
        }
        public static void UseJwtServices(this IApplicationBuilder app)
        {
            app.UseAuthentication();
            app.UseAuthorization();
        }

        public static string CreateToken(Dictionary<string, string> payload, bool IsRequest=true)
        {
            var token = GetJwtSecurityToken(payload, IsRequest);
            var jwtToken = new JwtSecurityTokenHandler().WriteToken(token);
            return jwtToken;
        }
        private static JwtSecurityToken GetJwtSecurityToken (Dictionary<string,string> payload,bool IsRequest = true)
        {
            var algorithm = SecurityAlgorithms.HmacSha256;
            // 2. 定义需要使用到的Claims
            List<Claim> claims = new()
            {
                new Claim("role","Admin")
            };
            foreach (string key in payload.Keys)
            {
                Claim claim = new Claim(key, payload[key]);
                claims.Add(claim);
            }

            // 3. 从 appsettings.json 中读取SecretKey
            var secretKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(IsRequest ? config["RequestSecurityKey"] : config["RefreshSecurityKey"]));
            // 4. 生成Credentials
            var signingCredentials = new SigningCredentials(secretKey, algorithm);
            // 5. 根据以上组件，生成token
            var token = new JwtSecurityToken(
                issuer: config["Issuer"],    //Issuer
                audience: config["Audience"],  //Audience
                claims: claims,                          //Claims,
                notBefore: DateTime.Now,                    //notBefore
                expires: DateTime.Now.AddMinutes(IsRequest ? config.GetValue<double>("RequestExpire") : config.GetValue<double>("RefreshExpire")),         //expires
                signingCredentials: signingCredentials
            );
            return token;
        }
        public static T GetData<T>(string JwtString,string key=null)
        {
            JwtString = JwtString.Replace("Bearer ", "");
            string[] jwtpart = JwtString.Split(".");
            if (string.IsNullOrEmpty(key))
            {
                return JsonConvert.DeserializeObject<T>(Base64UrlEncoder.Decode(jwtpart[1]));
            }
            else
            {
                return JObject.Parse(jwtpart[1])["key"].ToObject<T>();
            }
        }
        public static bool ValidateToken(string token, bool IsRequest = true)
        {
            var p = new TokenValidationParameters()
            {
                ValidateIssuer = config.GetValue<bool>("ValidIssuer"),
                ValidIssuer = config["Issuer"],
                ValidateAudience = config.GetValue<bool>("ValidAudience"),
                ValidAudience = config["Audience"],
                ValidateLifetime = config.GetValue<bool>("ValidateLifetime"),
                IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(IsRequest ? config["RequestSecurityKey"] : config["RefreshSecurityKey"]))
            };
            try
            {
                new JwtSecurityTokenHandler().ValidateToken(token, p, out SecurityToken securityToken);
                return true;
            }
            catch
            {
                return false;
            }
            
        }
    }
}
